//
//  Network.swift
//  ZZBase
//
//  Created by 张文晏 on 2018/9/21.
//  Copyright © 2018 张文晏. All rights reserved.
//  网络

import Moya

/// 后端返回数据结构(data 内为目标数据)
public struct ResponseData<T: Decodable>: Decodable {
    /// 是否返回成功
    var ret: Bool
    /// 返回数据
    var data: T?
    /// 错误码（`ret` 为 false 时才会有值）
    var errorCode: Int?
    /// 错误信息（`ret` 为 false 时才会有值）
    var errorMsg: String?
    /// 错误权限信息（`ret` 为 false 时才会有值）
    var errorAuthMsg: String?
}

/// 后端返回数据结构模板
public struct BaseResponseData: Decodable {
    /// 是否返回成功
    var ret: Bool
    /// 错误码（`ret` 为 false 时才会有值）
    var errorCode: Int?
    /// 错误信息（`ret` 为 false 时才会有值）
    var errorMsg: String?
    /// 错误权限信息（`ret` 为 false 时才会有值）
    var errorAuthMsg: String?
}

/// 网络请求错误
public typealias RequestError = (code: Int, msg: String)

/// 当前服务器地址
public var currentHost = "https://www.xinheyun.com"

public var uploadHost: String {
    return User.shared.systemConfig.storageFlag == 1 ?
        "https://storage.xinheyun.com" : "http://upload.qiniu.com"
}

fileprivate struct UploadResultItem: Decodable {}

extension MoyaProvider {
    public convenience init(timeout: TimeInterval = 20) {
        self.init(requestClosure: { (endpoint, closure) in
            if var urlRequest = try? endpoint.urlRequest() {
                urlRequest.timeoutInterval = timeout
                urlRequest.cachePolicy = .reloadIgnoringLocalCacheData
                var urlC: URLComponents?
                if let url = urlRequest.url {
                    urlC = URLComponents(url: url, resolvingAgainstBaseURL: true)
                }
                if urlC?.queryItems != nil {
                    urlC?.queryItems?.append(URLQueryItem(name: "from", value: "iOS"))
                } else {
                    urlC?.queryItems = [URLQueryItem(name: "from", value: "iOS")]
                }
                if let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String {
                    urlC?.queryItems?.append(URLQueryItem(name: "version", value: version))
                }
                urlRequest.url = urlC?.url
                closure(.success(urlRequest))
            } else {
                closure(.failure(MoyaError.requestMapping(endpoint.url)))
            }
        })
    }
    
    @discardableResult
    open func request<T: Decodable>(
        _ target: Target,
        decodeFromData: Bool = true,
        model: T.Type,
        progress: ProgressBlock? = .none,
        success: @escaping ((_ data: T) -> Void) = { _ in },
        failure: @escaping ((_ error: RequestError) -> Void) = { _ in }
        ) -> Cancellable {
        return request(target, progress: progress, completion: { result in
            switch result {
            case let .success(response):
                guard response.statusCode != 402 else {
                    NotificationCenter.default.post(name: NSNotification.Name("RELOGIN"), object: nil)
                    return
                }
                let decoder = JSONDecoder()
                do {
                    if target.baseURL.absoluteString == uploadHost { // upload
                        if response.statusCode >= 200 && response.statusCode < 300 {
                            let data = try decoder.decode(T.self, from: response.data)
                            success(data)
                            return
                        }
                    } else { // default
                        let item = try decoder.decode(BaseResponseData.self, from: response.data)
                        guard item.ret else {
                            if response.statusCode == 401 || response.statusCode == 402 {
                                failure((response.statusCode, item.errorAuthMsg ?? "无权限"))
                            } else {
                                failure((item.errorCode ?? -101, item.errorMsg ?? "返回错误"))
                            }
                            return
                        }
                        if decodeFromData {
                            let dataItem = try decoder.decode(ResponseData<T>.self, from: response.data)
                            if let data = dataItem.data {
                                success(data)
                            } else {
                                failure(RequestError(code: -100, msg: dataItem.errorMsg ?? "解析错误"))
                            }
                        } else {
                            let data = try decoder.decode(T.self, from: response.data)
                            success(data)
                        }
                    }
                } catch {
                    print("---------- 解析错误 ----------")
                    print(error)
                    print("-----------------------------")
                    let description: String
                    #if DEBUG
                    description = String(describing: error)
                    #else
                    description = "解析错误"
                    #endif
                    failure(( -100, description))
                }
            case let .failure(error):
                let requestError = (-201, error.errorDescription ?? "访问错误")
                failure(requestError)
            }
        })
    }
}

// MARK: - Api
let baseApiProvider = MoyaProvider<BaseApi>(timeout: 20)

enum BaseApi: TargetType {
    /// 登录
    case login(username: String, password: String)
    /// 退出登录
    case logout
    /// 获取系统设置
    case getSystemConfig
    /// 获取币种信息
    case getSupportCurrencies
    /// 检查版本
    case checkVersion(alias: String, mappingVersion: String)
    /// 获取图片占位信息
    case getImageInfo
    /// 上传图片
    case uploadImage(imageData: Data, mimeType: String, key: String, token: String?)
    
    var baseURL: URL {
        switch self {
        case .uploadImage:
            return URL(string: uploadHost) ?? (NSURL() as URL)
        default:
            return URL(string: currentHost) ?? (NSURL() as URL)
        }
    }
    
    var path: String {
        switch self {
        case .login:
            return "/api/user-center/auth"
        case .logout:
            return "/api/user-center/auth/quit"
        case .getSystemConfig:
            return "/api/user-center/system-config/fe"
        case .getSupportCurrencies:
            return "/api/user-center/system-config/currencyInfo/data/data-auth"
        case .checkVersion:
            return "/api/mobile-center/menu/checkMappingVersionByModuleAlias"
        case .getImageInfo:
            return "/api/image"
        case .uploadImage:
            return ""
        }
    }
    
    var method: Moya.Method {
        switch self {
        case .logout, .getSystemConfig, .getSupportCurrencies, .checkVersion, .getImageInfo:
            return .get
        case .login, .uploadImage:
            return .post
        }
    }
    
    var sampleData: Data {
        return Data()
    }
    
    var task: Task {
        switch self {
        case .login(let username, let password):
            let param = ["phone": username, "password": password]
            return .requestParameters(parameters: param, encoding: URLEncoding.default)
            
        case .checkVersion(let alias, let mappingVersion):
            let param = ["alias": alias, "mappingVersion": mappingVersion]
            return .requestParameters(parameters: param, encoding: URLEncoding.default)
            
        case .uploadImage(let imageData, let mimeType, let key, let token):
            var parts = [MultipartFormData]()
            parts.append(
                MultipartFormData(
                    provider: .data(imageData),
                    name: "file",
                    fileName: "file",
                    mimeType: mimeType
                )
            )
            if let keyData = key.data(using: .utf8) {
                parts.append(MultipartFormData(provider: .data(keyData), name: "key"))
            }
            if let tokenStr = token, let tokenData = tokenStr.data(using: .utf8) {
                parts.append(MultipartFormData(provider: .data(tokenData), name: "token"))
            }
            return .uploadMultipart(parts)
            
        case .logout, .getSystemConfig, .getSupportCurrencies, .getImageInfo:
            return .requestPlain
        }
    }
    
    var headers: [String : String]? {
        return nil
    }
}

// MARK: - Check version
public enum CheckVersionResult {
    case success
    case failure(msg: String)
}

/// 检查版本
///
/// - Parameters:
///   - alias: 模块别称
///   - version: 版本号
///   - resultClosure: 结果回调
public func checkVersion(alias: String, version: String, resultClosure: ((_ result: CheckVersionResult) -> Void)?) {
    baseApiProvider.request(
        BaseApi.checkVersion(alias: alias, mappingVersion: version),
        decodeFromData: false,
        model: BaseResponseData.self,
        success: { _ in
            resultClosure?(.success)
    }) { error in
        resultClosure?(.failure(msg: error.msg))
    }
}

// MARK: - Get image info
public struct ImageInfoItem: Decodable {
    public var name: String
    public var token: String?
}

@discardableResult
public func getImageInfo(success: @escaping (_ info: ImageInfoItem) -> Void, failure: @escaping (_ error: RequestError) -> Void) -> Cancellable {
    return baseApiProvider.request(
        BaseApi.getImageInfo,
        decodeFromData: true,
        model: ImageInfoItem.self,
        success: { info in
            success(info)
    }) { error in
        failure(error)
    }
}

// MARK: - Upload image
@discardableResult
public func uploadImage(image: UIImage, key: String, token: String?, progress: ProgressBlock? = .none, success: @escaping () -> Void, failure: @escaping (_ error: RequestError) -> Void) -> Cancellable {
    return baseApiProvider.request(
        BaseApi.uploadImage(
            imageData: image.jpegData(compressionQuality: 0.5)!,
            mimeType: "image/jpeg",
            key: key,
            token: token
        ),
        decodeFromData: false,
        model: UploadResultItem.self,
        progress: progress,
        success: { _ in
            success()
    }) { error in
        failure(error)
    }
    
}
